package com.klangner.go.app.strategy;

import java.util.Enumeration;
import java.util.Vector;

import com.klangner.game.board.IBoard;
import com.klangner.go.app.rules.BoardModel;
import com.klangner.go.app.rules.GoConstants;
import com.klangner.go.app.rules.StonesGroupOld;


public class AtariGoEstimatorCho extends BasicGoEstimator {

    public AtariGoEstimatorCho() {
        super();
    }

        /**
         * BasicGoEstimator method implementation
         * Returns the state of the game in curent <position>, i.e:
         * BlackWIN, WhiteWIN, JIGO or InProgress
         */
    public int checkPositionState(BoardModel position) {
            // here is the update: since we use info about captured stones,
            // it's much easier
        if ((position.getNrBlackPrisoners() == 0) &&
            (position.getNrWhitePrisoners() == 0))
            return GoConstants.InProgress;

            // Check for a White win.
        else if (((position.getNrBlackPrisoners() > 0) &&
                  (position.getNrWhitePrisoners() == 0)))
            return GoConstants.WhiteWIN;

            // Check for a Black win.
        else if (((position.getNrBlackPrisoners() == 0) &&
                  (position.getNrWhitePrisoners() > 0)))
            return GoConstants.BlackWIN;

        else {
            System.err.println("WRONG: checkPositionState: both black and white stones captured!");
            System.err.println(position.toString());
            System.err.println("Num BLACK prisoners = " +
                               position.getNrBlackPrisoners());
            System.err.println("Num WHITE prisoners = " +
                               position.getNrWhitePrisoners());

            System.exit(1);
            return -10001;
        }
    }

        // BasicGoEstimator method implementation
        // returns a heuristic evaluation of the <position> from the point of view
        // of the <color> player
    protected float heuristicPositionEstimation(BoardModel position,
                                                int color) {
        return balancedPositionEstimation(position, color);
    }

        // Will estimate the value of <position> from the point of view
        // of the <color> player.
    protected float balancedPositionEstimation(BoardModel position,
                                               int color) {
//        Debug.log("Started balancedPositionEstimation, color = " + color);

        int myMinimum, oppMinimum, myCurrent, oppCurrent;
        Vector<StonesGroupOld> myGroups = null, oppGroups = null;

        if (color == IBoard.BLACK_STONE) {
            myGroups = position.getBlackGroups();
            oppGroups = position.getWhiteGroups();
        }
        else if (color == IBoard.WHITE_STONE) {
            myGroups = position.getWhiteGroups();
            oppGroups = position.getBlackGroups();
        }
        else {
            System.out.println("Unknown color: " + color);
            System.exit(1);
        }

        StonesGroupOld myMinGroup = null;

            // compute my minimum
        myMinimum = Integer.MAX_VALUE;
        Enumeration myEnum = myGroups.elements();
        for (;myEnum.hasMoreElements();) {
            StonesGroupOld curGroup = (StonesGroupOld)myEnum.nextElement();
            myCurrent = curGroup.nrLiberties();
            if (myCurrent < myMinimum) {
                myMinimum = myCurrent;
                myMinGroup = curGroup;
            }
        }

//         if (myMinGroup != null) {
//             System.out.println( myMinGroup.color() + " group = (" + myMinGroup.stones().first().x + ", " + myMinGroup.stones().first().x + "); libs = " + myMinimum );
//         } else {
//             System.out.println( "no group for " + color );
//         }

        StonesGroupOld oppMinGroup = null;

            // compute opp(onent's) minimum
        oppMinimum = Integer.MAX_VALUE;
        Enumeration oppEnum = oppGroups.elements();
        for (;oppEnum.hasMoreElements();) {
            StonesGroupOld curGroup = (StonesGroupOld)oppEnum.nextElement();
            oppCurrent = curGroup.nrLiberties();
            if (oppCurrent < oppMinimum) {
                oppMinimum = oppCurrent;
                oppMinGroup = curGroup;
            }
        }

/*
        Debug.log( " Balanced estimator:  myMinimum = " + myMinimum +
                   ", oppMinimum = " + oppMinimum + " for " + color +
                   " in " + position );
*/

        float result = 0;

        if (myMinimum == Integer.MAX_VALUE && oppMinimum == Integer.MAX_VALUE )
                // no groups on board
        {
            result = 0;
        }
        else if (myMinimum == Integer.MAX_VALUE)
                // no groups of mine
        {
            result =  -oppMinimum;
        }
        else if (oppMinimum == Integer.MAX_VALUE)
                // no groups of opponent's
        {
            result = myMinimum;
        }
        else
        {
            if (myMinimum == 0) {
                    // programming error
                System.exit(1);
            }

            float rawResult = myMinimum - oppMinimum; //- (float)1/myMinimum;
                // The 1/myMinimum is to differentiate when myMinimum == oppMinimum

            final int inAtariPenalty = 100;
                // When somebody is in atari


            if (position.getTurnColor() == color && oppMinimum == 1)
                result = GoStrategyConstants.VictoryValue;
            else if (position.getTurnColor() != color && myMinimum == 1)
                result = GoStrategyConstants.LossValue;
            else if (position.getTurnColor() != color && oppMinimum == 1)
                result = inAtariPenalty;
            else if (position.getTurnColor() == color && myMinimum == 1)
                result = -inAtariPenalty;
            else
                result = rawResult;
        }

//        Debug.log( " balancedPositionEstimation result = " + result );
        return result;
    }

        /**
         * Returns the number of liberties of the weakest group of the color player.
         */
    int computeWeakestGroupLibs( BoardModel position, int color ) {
        int minimum, current;
        Vector<StonesGroupOld> groups = null;

        if (color == IBoard.WHITE_STONE) {
            groups = position.getWhiteGroups();
        }
        else if (color == IBoard.BLACK_STONE) {
            groups = position.getBlackGroups();
        }
        else {
            System.out.println("Unknown color: " + color);
            System.exit(1);
        }

            // compute minimum
        minimum = Integer.MAX_VALUE;

        Enumeration gEnum = groups.elements();

        for ( ; gEnum.hasMoreElements() ; ) {
            current = ((StonesGroupOld)gEnum.nextElement()).nrLiberties();
            if ( current < minimum )
                minimum = current;
        }

        return minimum;
    }

    private static String printResult( float actual,
                                       float expected,
                                       boolean wasSuccess ) {
        String result = "Test result";

        if ( wasSuccess ) {
            result += " SUCCESS";
        } else {
            result += " FAILURE";
        }

        result += "\nActual = " + actual + ", Expected = " + expected;

        return result;
    }


        // Returns true on success, false otherwise.
    public static boolean test() {
        float actual = 0;
        float expected = 0;

        AtariGoEstimatorCho estimator = new AtariGoEstimatorCho();

        BoardModel pos1 = new BoardModel( 2 );
        System.out.println( pos1.toString() );
            /*
              . .
              . .
            */
        if (estimator.balancedPositionEstimation(pos1,
                                                 IBoard.BLACK_STONE) != 0) {
            return false;
        }
        if (estimator.balancedPositionEstimation(pos1,
                                                 IBoard.WHITE_STONE) != 0) {
            return false;
        }
        System.out.println( "OK" );


        pos1.makeMove(0, 0, IBoard.BLACK_STONE);
        System.out.println( pos1.toString() );
            /*
              # .
              . .
            */
        if (estimator.balancedPositionEstimation(pos1,
                                                 IBoard.BLACK_STONE) != 2)
        {
            System.out.println("move 1, black");
            return false;
        }

        if (estimator.balancedPositionEstimation(pos1,
                                                 IBoard.WHITE_STONE) != -2)
        {
            System.out.println("move 1, white");
            return false;
        }
        System.out.println( "OK" );

        pos1.makeMove(1, 1, IBoard.WHITE_STONE);
        System.out.println( pos1.toString() );
            /*
              # .
              . 0
            */
        actual = estimator.balancedPositionEstimation(pos1,
                                                      IBoard.BLACK_STONE);
        expected = (float)-1/2;
        if ( actual != expected)
        {
            System.out.println("move 2, black");
            System.out.println( printResult( actual, expected, false ) );
            return false;
        }

        actual = estimator.balancedPositionEstimation(pos1,
                                                      IBoard.WHITE_STONE);
        expected = (float)-1/2;
        if (actual != expected)
        {
            System.out.println("move 2, white");
            System.out.println( printResult( actual, expected, false ) );
            return false;
        }
        System.out.println( "OK" );

        pos1.makeMove(0, 1, IBoard.BLACK_STONE);
        System.out.println( pos1.toString() );
            /*
              # .
              # 0
            */
        actual = estimator.balancedPositionEstimation(pos1,
                                                      IBoard.BLACK_STONE);
        expected = GoStrategyConstants.LossValue;
        if ( actual != expected )
        {
            System.out.println("move 3, black");
            System.out.println( printResult( actual, expected, false ) );
            return false;
        }

        actual = estimator.balancedPositionEstimation(pos1,
                                                      IBoard.WHITE_STONE);
        expected = GoStrategyConstants.VictoryValue;
        if (actual != expected)
        {
            System.out.println("move 3, white");
            System.out.println( printResult( actual, expected, false ) );
            return false;
        }
        System.out.println( "OK" );

        pos1.makeMove(1, 0, IBoard.WHITE_STONE);
        System.out.println( pos1.toString() );
            /*
              . 0
              . 0
            */
        if (estimator.balancedPositionEstimation(pos1,
                                                 IBoard.BLACK_STONE) != -2)
        {
            System.out.println("move 4, black");
            return false;
        }

        if (estimator.balancedPositionEstimation(pos1,
                                                 IBoard.WHITE_STONE) != 2)
        {
            System.out.println("move 4, white");
            return false;
        }
        System.out.println( "OK" );

        return true;
    }

}
